`timescale 1ns / 1ps
/******************************************************************************
*                                                                             *
* UTICA softcore v0.1                                                         *
*                                                                             *
* Copyright (c) 2012 Andrew D. Zonenberg                                      *
* All rights reserved.                                                        *
*                                                                             *
* Redistribution and use in source and binary forms, with or without modifi-  *
* cation, are permitted provided that the following conditions are met:       *
*                                                                             *
*    * Redistributions of source code must retain the above copyright notice  *
*      this list of conditions and the following disclaimer.                  *
*                                                                             *
*    * Redistributions in binary form must reproduce the above copyright      *
*      notice, this list of conditions and the following disclaimer in the    *
*      documentation and/or other materials provided with the distribution.   *
*                                                                             *
*    * Neither the name of the author nor the names of any contributors may be*
*      used to endorse or promote products derived from this software without *
*      specific prior written permission.                                     *
*                                                                             *
* THIS SOFTWARE IS PROVIDED BY THE AUTHORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF        *
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN     *
* NO EVENT SHALL THE AUTHORS BE HELD LIABLE FOR ANY DIRECT, INDIRECT,         *
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT    *
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,   *
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY       *
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT         *
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF    *
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.           *
*                                                                             *
******************************************************************************/

//Uncomment this line to enable printing of stalls
//`define UTICA_TRACE_STALL

/**
	@file UticaCPUExecuteStage.v
	@author Andrew D. Zonenberg
	@brief Execute stage of the pipeline
 */
module UticaCPUExecuteStage(
	clk,
	hold,
	dbus_addr, dbus_wmask, dbus_wr, dbus_rd, dbus_dout,
	decode_pc,
	execute_pc, execute_jump_raw, execute_jump_final,
	execute_rs_raw, execute_rt_raw,
	execute_imm, execute_imm_sx, execute_imm_zx,
	execute_jumping,
	execute_rsid, execute_rtid, execute_rdid, execute_shamt, execute_regwrite,
	execute_rs_forwarded, execute_rt_forwarded,
	execute_div, execute_divu, execute_jr, execute_jalr,
	execute_opcode,
	mem_rdval, mem_rdid, mem_regwrite, mem_lw, mem_lb, mem_lbu, mem_lh, mem_lhu, mem_mult, mem_multu,
	postwb2_mult, postwb2_multu, div_active,
	writeback_rdid, writeback_rdval, writeback_regwrite, writeback_memread, writeback_cachemiss,
	div_quot, div_rem, div_done,
	mdu_hi, mdu_lo,
	stall_for_mem_rs, stall_for_mem_rt, execute_memread,
	mem_overflow_exception, mem_lowaddr,
	multout_s, multout_u,
	execute_rtype, execute_func, execute_bubble,
	cp0_r_data,
	mem_cp0_w_regid, mem_cp0_w_enable, mem_cp0_w_data,
	mdu_lo_forwarded, mdu_hi_forwarded, mem_mfhi, mem_mflo,
	writeback_mfhi, writeback_mflo, writeback_lb, writeback_lw, writeback_lh, writeback_lbu, writeback_lhu,
	writeback_rdval_forwarded,
	execute_cachemiss
    );

	////////////////////////////////////////////////////////////////////////////////////////////////
	// IO declarations
	
	input wire clk;
	input wire hold;
	
	output reg[31:0] dbus_addr = 0;
	output reg[3:0] dbus_wmask = 0;
	output reg dbus_wr = 0;
	output reg dbus_rd = 0;
	output reg[31:0] dbus_dout = 0;
	
	input wire[31:0] decode_pc;
	
	output reg[31:0] execute_pc = 0;
	input wire[31:0] execute_jump_raw;
	output wire[31:0] execute_jump_final;
	
	input wire[31:0] execute_rs_raw;
	input wire[31:0] execute_rt_raw;
	
	input wire[4:0] execute_rsid;
	input wire[4:0] execute_rtid;
	
	output wire[31:0] execute_rs_forwarded;
	output wire[31:0] execute_rt_forwarded;
	
	input wire[15:0] execute_imm;
	input wire[31:0] execute_imm_sx;
	input wire[31:0] execute_imm_zx;
	
	input wire execute_jumping;
	
	input wire execute_regwrite;
	input wire[4:0] execute_rdid;
	
	input wire[4:0] execute_shamt;
	
	input wire execute_jr;
	input wire execute_jalr;
	input wire execute_div;
	input wire execute_divu;
	
	input wire[5:0] execute_opcode;

	output reg[31:0] mem_rdval = 0;			//Output of this instruction
	output reg[4:0] mem_rdid = 0;				//Register being written to
	output reg mem_regwrite = 0;				//Asserted if we are doing a reg write
	output reg mem_lw = 0;
	output reg mem_lb = 0;
	output reg mem_lbu = 0;
	output reg mem_lh = 0;
	output reg mem_lhu = 0;
	
	output reg mem_mult = 0;
	output reg mem_multu = 0;
	
	input wire postwb2_mult;
	input wire postwb2_multu;
	
	output reg div_active = 0;
	
	input wire[4:0] writeback_rdid;
	input wire[31:0] writeback_rdval;
	input wire writeback_regwrite;
	input wire writeback_memread;
	
	output wire[31:0] div_quot;
	output wire[31:0] div_rem;
	output wire div_done;
	
	input wire[31:0] mdu_hi;
	input wire[31:0] mdu_lo;
	
	output wire stall_for_mem_rs;
	output wire stall_for_mem_rt;
	
	output wire execute_memread;
	
	output reg mem_overflow_exception = 0;		//output for debugging so synthesis doesnt optimize it out
	output reg[1:0] mem_lowaddr = 0;				//Low order bits of address (not aligned like dbus_addr is)
	
	output wire[63:0] multout_u;
	output wire[63:0] multout_s;
	
	input wire execute_rtype;
	input wire[5:0] execute_func;
	
	input wire execute_bubble;
	
	input wire[31:0] cp0_r_data;
	
	output reg[4:0] mem_cp0_w_regid = 0;
	output reg mem_cp0_w_enable = 0;
	output reg[31:0] mem_cp0_w_data = 0;
	
	input wire writeback_cachemiss;
	
	output reg[31:0] mdu_lo_forwarded = 0;
	output reg[31:0] mdu_hi_forwarded = 0;
	
	output reg mem_mfhi = 0;
	output reg mem_mflo = 0;
	
	input wire writeback_mfhi;
	input wire writeback_mflo;
	
	input wire writeback_lw;
	input wire writeback_lb;
	input wire writeback_lh;
	input wire writeback_lbu;
	input wire writeback_lhu;
	
	input wire[31:0] writeback_rdval_forwarded;
	
	output reg execute_cachemiss = 0;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Function table for R-type instructions
	//TODO: don't duplicate this
	localparam FUNC_ADD  = 6'b100000;
	localparam FUNC_ADDU = 6'b100001;
	localparam FUNC_AND  = 6'b100100;
	localparam FUNC_DIV	= 6'b011010;
	localparam FUNC_DIVU	= 6'b011011;
	localparam FUNC_JR	= 6'b001000;
	localparam FUNC_JALR	= 6'b001001;		//Checked several sources and found two conflicting encodings.
														//This is the encoding used by gcc and the official MIPS manuals.
	localparam FUNC_MFHI = 6'b010000;
	localparam FUNC_MFLO = 6'b010010;
	localparam FUNC_MULT	= 6'b011000;
	localparam FUNC_MULTU= 6'b011001;
	localparam FUNC_NOR	= 6'b100111;
	localparam FUNC_OR   = 6'b100101;
	localparam FUNC_SLL	= 6'b000000;
	localparam FUNC_SLLV = 6'b000100;
	localparam FUNC_SLT	= 6'b101010;
	localparam FUNC_SLTU	= 6'b101011;
	localparam FUNC_SRA	= 6'b000011;
	localparam FUNC_SRL 	= 6'b000010;
	localparam FUNC_SRLV	= 6'b000110;
	localparam FUNC_SUB  = 6'b100010;
	localparam FUNC_SUBU = 6'b100011;
	localparam FUNC_XOR	= 6'b100110;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Opcode table
	localparam OP_ADDI	= 6'b001000;
	localparam OP_ADDIU	= 6'b001001;
	localparam OP_ANDI	= 6'b001100;
	localparam OP_BEQ		= 6'b000100;
	localparam OP_BGTZ	= 6'b000111;
	localparam OP_BLEZ	= 6'b000110;
	localparam OP_BRANCH	= 6'b000001;	//BGEZ if rt =		00001
													//BGEZAL if rt =	10001
													//BLTZ if rt =		00000
													//BLTZAL if rt =	10000
	localparam OP_BNE		= 6'b000101;
	localparam OP_COPROC	= 4'b0100;
	localparam OP_J 		= 6'b000010;
	localparam OP_JAL		= 6'b000011;
	localparam OP_LB		= 6'b100000;
	localparam OP_LBU		= 6'b100100;
	localparam OP_LH		= 6'b100001;
	localparam OP_LHU		= 6'b100101;
	localparam OP_LW		= 6'b100011;
	localparam OP_LUI		= 6'b001111;
	localparam OP_ORI		= 6'b001101;
	localparam OP_RTYPE	= 6'b000000;
	localparam OP_SB		= 6'b101000;
	localparam OP_SH		= 6'b101001;
	localparam OP_SLTI	= 6'b001010;
	localparam OP_SLTIU	= 6'b001011;
	localparam OP_SW		= 6'b101011;
	localparam OP_XORI	= 6'b001110;
	
	localparam OP_COPROC_0	= 6'b010000;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// CP0 opcode table
	localparam CP0_MF		= 5'b00000;
	localparam CP0_MT		= 5'b00100;
	
	////////////////////////////////////////////////////////////////////////////////////////////////
	// Execution logic
	
	wire mem_memop;
	assign mem_memop = mem_lw | mem_lb | mem_lbu | mem_lh | mem_lhu;
	
	assign execute_memread = 	execute_opcode == OP_LB | execute_opcode == OP_LBU |
										execute_opcode == OP_LH | execute_opcode == OP_LHU |
										execute_opcode == OP_LW;
	
	//Forwarding logic
	UticaCPUPipelineForwarding rs_forwarding (
		.dstreg(execute_rsid), 
		.rawval(execute_rs_raw), 
		.mem_regwrite(mem_regwrite), 
		.mem_rdid(mem_rdid), 
		.mem_rdval(mem_rdval),
		.mem_memop(mem_memop),
		.mem_mfhi(mem_mfhi), 
		.mem_mflo(mem_mflo), 
		.writeback_mfhi(writeback_mfhi),
		.writeback_mflo(writeback_mflo),
		.writeback_regwrite(writeback_regwrite), 
		.writeback_rdid(writeback_rdid), 
		.writeback_rdval(writeback_rdval), 			
		.writeback_memread(writeback_memread), 		
		.forwarded(execute_rs_forwarded),
		.stall_for_mem(stall_for_mem_rs)
		);
	UticaCPUPipelineForwarding rt_forwarding (
		.dstreg(execute_rtid), 
		.rawval(execute_rt_raw), 
		.mem_regwrite(mem_regwrite), 
		.mem_rdid(mem_rdid), 
		.mem_rdval(mem_rdval), 
		.mem_memop(mem_memop),
		.mem_mfhi(mem_mfhi), 
		.mem_mflo(mem_mflo), 
		.writeback_mfhi(writeback_mfhi),
		.writeback_mflo(writeback_mflo),
		.writeback_regwrite(writeback_regwrite), 
		.writeback_rdid(writeback_rdid), 
		.writeback_rdval(writeback_rdval), 	
		.writeback_memread(writeback_memread), 		
		.forwarded(execute_rt_forwarded),
		.stall_for_mem(stall_for_mem_rt)
		);
		
	//Compute jump address
	//Always absolute address except in case of jr instruction
	assign execute_jump_final = (execute_jr | execute_jalr) ? execute_rs_forwarded : execute_jump_raw;
		
	//Assign names to some arithmetic stuff so we can check overflow better
	wire[32:0] execute_rs_plus_rt;
	assign execute_rs_plus_rt = execute_rs_forwarded + execute_rt_forwarded;
	wire[32:0] execute_rs_minus_rt;
	assign execute_rs_minus_rt =	{execute_rs_forwarded[31], execute_rs_forwarded} -
											{execute_rt_forwarded[31], execute_rt_forwarded};
	wire[32:0] execute_rs_plus_imm;
	assign execute_rs_plus_imm = execute_rs_forwarded + execute_imm_sx;
	
	wire[31:0] execute_mem_addr;
	assign execute_mem_addr = execute_rs_forwarded + execute_imm_sx;
	
	//Multiplier
	UnsignedPipelinedMultiplier multu(
		.clk(clk), // input clk
		.a(execute_rs_forwarded),
		.b(execute_rt_forwarded),
		.ce(1'b1), 		//TODO clock gating
		.p(multout_u)
		);
	SignedPipelinedMultiplier mults(
		.clk(clk), // input clk
		.a(execute_rs_forwarded),
		.b(execute_rt_forwarded),
		.ce(1'b1), 		//TODO clock gating
		.p(multout_s)
		);
		
	//Divider
	NonPipelinedDivider divider (
		.clk(clk), 
		.start(execute_div | execute_divu), 
		.dend(execute_rs_forwarded), 
		.dvsr(execute_rt_forwarded), 
		.quot(div_quot), 
		.rem(div_rem), 
		//.busy(div_busy), 
		.done(div_done), 
		.sign(execute_div)
		);

		
	//Forwarding for lo/hi
	always @(postwb2_mult, postwb2_multu, mdu_lo, mdu_hi, multout_u, multout_s) begin

		if(postwb2_mult) begin
			mdu_lo_forwarded <= multout_s[31:0];
			mdu_hi_forwarded <= multout_s[63:32];
		end
		else if(postwb2_multu) begin
			mdu_lo_forwarded <= multout_u[31:0];
			mdu_hi_forwarded <= multout_u[63:32];
		end
		else begin
			mdu_lo_forwarded <= mdu_lo;
			mdu_hi_forwarded <= mdu_hi;
		end
			
	end
	
	//D-side cache miss handling
	//This block here just saves state
	reg[31:0] mem_pc = 0;
	reg[31:0] writeback_pc = 0;
	reg[31:0] writeback_dbus_addr = 0;
	reg[31:0] execute_cachemiss_addr = 0;
	reg[4:0] execute_cachemiss_rdid;
	reg[4:0] execute_cachemiss_loadtypes = 0;
	reg[1:0] writeback_lowaddr = 0;
	reg[1:0] execute_cachemiss_lowaddr = 0;
	always @(posedge clk) begin
		
		//dbus is valid for mem stage
		writeback_dbus_addr <= dbus_addr;
		execute_cachemiss_addr <= writeback_dbus_addr;
		writeback_lowaddr <= mem_lowaddr;
		
		//save $pc so we can resume afterwards
		mem_pc <= execute_pc;
		writeback_pc <= mem_pc;
		
		execute_cachemiss <= writeback_cachemiss;
		execute_cachemiss_rdid <= writeback_rdid;
		execute_cachemiss_lowaddr <= writeback_lowaddr;
		execute_cachemiss_loadtypes <= {writeback_lw, writeback_lh, writeback_lhu, writeback_lb, writeback_lbu};
		
	end
		
	//Main execution logic
	//At this point everything is fully decoded, just do stuff!
	always @(posedge clk) begin	
	
		//Push pc down the pipeline
		execute_pc <= decode_pc;
	
		//Turn off writes to regfile if we aren't doing anything
		//rdid and rdval can be not cleared if this becomes a performance issue
		//but it looks cleaner in simulation this way
		mem_regwrite <= 0;
		mem_rdid <= 0;
		mem_rdval <= 0;
		mem_overflow_exception <= 0;
		
		dbus_addr <= 0;
		dbus_wmask <= 0;
		dbus_wr <= 0;
		dbus_rd <= 0;
		dbus_dout <= 0;
	
		if(div_done)
			div_active <= 0;

		//BUGFIX: clear these even if bubbling
		mem_lw <= 0;
		mem_lb <= 0;
		mem_lbu <= 0;
		mem_lh <= 0;
		mem_lhu <= 0;
		mem_mult <= 0;
		mem_multu <= 0;
		
		mem_mfhi <= 0;
		mem_mflo <= 0;
		
		//Processing a cache miss? Send it out the data bus
		if(execute_cachemiss) begin
			dbus_addr <= execute_cachemiss_addr;
			dbus_rd <= 1;
			
			mem_regwrite <= 1;
			mem_rdid <= execute_cachemiss_rdid;
			mem_lowaddr <= execute_cachemiss_lowaddr;
			
			mem_lw <= execute_cachemiss_loadtypes[4];
			mem_lh <= execute_cachemiss_loadtypes[3];
			mem_lhu <= execute_cachemiss_loadtypes[2];
			mem_lb <= execute_cachemiss_loadtypes[1];
			mem_lbu <= execute_cachemiss_loadtypes[0];
		end
	
		else if(!hold && !execute_bubble) begin
			
			mem_rdid <= execute_rdid;
			mem_regwrite <= execute_regwrite;
			
			//DEBUG - trace the instruction
			//$display("Executing instruction at 0x%x", execute_pc);
			
			//R-type instructions
			if(execute_rtype) begin
				case(execute_func)
					
					FUNC_ADD: begin
						mem_rdval <= execute_rs_plus_rt[31:0];
				
						if(execute_rs_plus_rt[32])
							mem_overflow_exception <= 1;
					end
					
					FUNC_ADDU:	mem_rdval <= execute_rs_plus_rt[31:0];
					FUNC_AND:	mem_rdval <= execute_rs_forwarded & execute_rt_forwarded;
					
					FUNC_DIV: begin
						div_active <= 1;
						//TODO: cancel pending mult if it could potentially finish before we do
					end
					
					FUNC_DIVU: begin
						div_active <= 1;
						//TODO: cancel pending mult if it could potentially finish before we do
					end
					
					FUNC_JR: begin
						//nothing here but need to not flag as invalid
					end
					
					FUNC_JALR:	mem_rdval <= execute_pc + 32'h8;	//8, not 4!
																				//+4 is delay slot instruction which already executed
					
					FUNC_MFHI:	begin
						mem_rdval <= mdu_hi_forwarded;
						mem_mfhi <= 1;
					end
					
					FUNC_MFLO:	begin
						mem_rdval <= mdu_lo_forwarded;
						mem_mflo <= 1;
					end
					
					FUNC_MULT: begin
						mem_mult <= 1;
						div_active <= 0;
					end
					
					FUNC_MULTU: begin
						mem_multu <= 1;
						div_active <= 0;
					end
					
					FUNC_NOR:	mem_rdval <= ~(execute_rs_forwarded | execute_rt_forwarded);
					
					FUNC_OR:		mem_rdval <= execute_rs_forwarded | execute_rt_forwarded;
					
					FUNC_SLL:	mem_rdval <= execute_rt_forwarded << execute_shamt;
					
					FUNC_SLLV:	mem_rdval <= execute_rt_forwarded << execute_rs_forwarded;
					
					FUNC_SLT: begin
						if($signed(execute_rs_forwarded) < $signed(execute_rt_forwarded))
							mem_rdval <= 1;
						else	
							mem_rdval <= 0;
					end
					
					FUNC_SLTU: begin
						if(execute_rs_forwarded < execute_rt_forwarded)
							mem_rdval <= 1;
						else	
							mem_rdval <= 0;
					end
					
					FUNC_SRA:	mem_rdval <= (32'hFFFFFFFF << (6'd32 - execute_shamt)) | (execute_rt_forwarded >> execute_shamt);
					
					FUNC_SRL:	mem_rdval <= execute_rt_forwarded >> execute_shamt;
					
					FUNC_SRLV:	mem_rdval <= execute_rt_forwarded >> execute_rs_forwarded;
					
					FUNC_SUB: begin
						mem_rdval <= execute_rs_minus_rt[31:0];
						
						if(execute_rs_minus_rt[32])
							mem_overflow_exception <= 1;
					end
					
					FUNC_SUBU: 	mem_rdval <= execute_rs_minus_rt[31:0];
					
					FUNC_XOR:	mem_rdval <= execute_rs_forwarded ^ execute_rt_forwarded;
					
					//TODO: trigger exception
					default: begin
						$display("WARNING: Executing invalid R-type instruction %b, treating as no-op for now (t=%d ns)",
							execute_func, $time());
					end
				endcase
			end
			
			else begin
				case(execute_opcode)
					OP_ADDI: begin
						mem_rdval <= execute_rs_plus_imm[31:0];
					
						if(execute_rs_plus_imm[32])
							mem_overflow_exception <= 1;
					end
					
					OP_ADDIU: 	mem_rdval <= execute_rs_plus_imm[31:0];
					OP_ANDI:		mem_rdval <= execute_rs_forwarded & execute_imm_zx;
					
					OP_BEQ: begin
						//nothing here but need to not throw error
					end
					
					OP_BNE: begin
						//nothing here but need to not throw error
					end
					
					OP_BGTZ: begin
						//nothing here but need to not throw error
					end
					
					OP_BLEZ: begin
						//nothing here but need to not throw error
					end
					
					//If high order bit of rt is set, it's some kind of JAL
					OP_BRANCH: begin
						if(execute_rtid[4]) begin
							mem_rdval <= execute_pc + 4;
							mem_regwrite <= execute_jumping;	//only enable if we're actually doing the jump
						end
					end
					
					//Coprocessor 0
					OP_COPROC_0: begin
						case(execute_rsid)
							CP0_MF: begin
								mem_rdid <= execute_rtid;	//Break with tradition - destination is NOT rdid!
								mem_regwrite <= 1;
								mem_rdval <= cp0_r_data;
							end
							
							CP0_MT: begin
								mem_cp0_w_regid <= execute_rdid;
								mem_cp0_w_enable <= 1;
								mem_cp0_w_data <= execute_rt_forwarded;
								mem_regwrite <= 0;
							end
						endcase
					end
				
					OP_J: begin
						//nothing here but need to not throw error
					end
					
					OP_JAL: 		mem_rdval <= execute_pc + 8;		//8, not 4!
																				//+4 is delay slot instruction which already executed
					OP_LUI:		mem_rdval <= {execute_imm, 16'h0};
					
					OP_LB: begin
						//Store the actual low-order address bits so that we can mux stuff out
						mem_lowaddr <= execute_mem_addr[1:0];
						
						//Just like lw, but we word-align the address and sort the rest out when it comes back
						dbus_rd <= 1;
						dbus_addr <= {execute_mem_addr[31:2], 2'b0};
						mem_lb <= 1;
					end
					
					OP_LBU: begin
						//Store the actual low-order address bits so that we can mux stuff out
						mem_lowaddr <= execute_mem_addr[1:0];
						
						//Just like lw, but we word-align the address and sort the rest out when it comes back
						dbus_rd <= 1;
						dbus_addr <= {execute_mem_addr[31:2], 2'b0};
						mem_lbu <= 1;
					end
					
					OP_LH: begin
						//Store the actual low-order address bits so that we can mux stuff out
						mem_lowaddr <= execute_mem_addr[1:0];
						
						//Just like lh, but we word-align the address and sort the rest out when it comes back
						dbus_rd <= 1;
						dbus_addr <= {execute_mem_addr[31:2], 2'b0};
						mem_lh <= 1;
					end
					
					OP_LHU: begin
						//Store the actual low-order address bits so that we can mux stuff out
						mem_lowaddr <= execute_mem_addr[1:0];
						
						//Just like lh, but we word-align the address and sort the rest out when it comes back
						dbus_rd <= 1;
						dbus_addr <= {execute_mem_addr[31:2], 2'b0};
						mem_lhu <= 1;	
					end
					
					OP_LW: begin
						//Unaligned address? Something's wrong
						if(execute_mem_addr[1:0] != 2'b0) begin
							$display("[UticaCPU] ERROR: Bad unaligned word load (address %x)", execute_mem_addr);
						end

						mem_lowaddr <= 2'b0;
						
						dbus_rd <= 1;
						dbus_addr <= execute_mem_addr;
						mem_lw <= 1;
					end
					
					OP_ORI:		mem_rdval <= execute_rs_forwarded | execute_imm_zx;
					
					OP_RTYPE: begin
						//should never happen but here just to avoid confusion
						//Can happen if the I-side L1 misses, data will be a nop
						if(execute_func != 0 || execute_regwrite != 0)
							$display("WARNING: OP_RTYPE found with execute_rtype not asserted (func = %b)", execute_func);
					end
					
					OP_SLTI: begin
						if($signed(execute_rs_forwarded) < $signed(execute_imm_sx))
							mem_rdval <= 1;
						else	
							mem_rdval <= 0;
					end
					
					OP_SLTIU: begin
						if(execute_rs_forwarded < execute_imm_sx)
							mem_rdval <= 1;
						else	
							mem_rdval <= 0;
					end
					
					OP_SB: begin
						//Just like sw, but we word-align the address and sort the rest out with the bitmask
						dbus_addr <= {execute_mem_addr[31:2], 2'b0};
						dbus_wr <= 1;
						
						//Compute the bitmask for the write
						case(execute_mem_addr[1:0])
							2'd0: begin
								dbus_wmask <= 4'b0001;
								dbus_dout <= {24'h0, execute_rt_forwarded[7:0]};
							end
							2'd1: begin
								dbus_wmask <= 4'b0010;
								dbus_dout <= {16'h0, execute_rt_forwarded[7:0], 8'h0};
							end
							2'd2: begin
								dbus_wmask <= 4'b0100;
								dbus_dout <= {8'h0, execute_rt_forwarded[7:0], 16'h0};
							end
							2'd3: begin
								dbus_wmask <= 4'b1000;
								dbus_dout <= {execute_rt_forwarded[7:0], 24'h0};
							end
						endcase
					end
					
					OP_SH: begin
						//Just like sw, but we word-align the address and sort the rest out with the bitmask
						dbus_addr <= {execute_mem_addr[31:2], 2'b0};
						dbus_wr <= 1;
						
						//Compute the bitmask for the write
						if(execute_mem_addr[1]) begin
							dbus_wmask <= 4'b1100;
							dbus_dout <= {execute_rt_forwarded[15:0], 16'h0};
						end
						else begin
							dbus_wmask <= 4'b0011;
							dbus_dout <= {16'h0, execute_rt_forwarded[15:0]};
						end
					end
					
					OP_SW: begin
						//Unaligned address? Something's wrong
						if(execute_mem_addr[1:0] != 2'b0) begin
							$display("[UticaCPU] ERROR: Bad unaligned word load (address %x)", execute_mem_addr);
						end
							
						dbus_addr <= execute_mem_addr;
						dbus_wmask <= 4'hf;
						dbus_wr <= 1;
						dbus_dout <= execute_rt_forwarded;
					end
					
					OP_XORI: begin
						mem_rdval <= execute_rs_forwarded ^ execute_imm_zx;
					end
					
					//TODO: trigger exception
					default: begin
						$display("WARNING: Executing invalid instruction %b, treating as no-op for now (t=%d)",
							execute_opcode, $time());
					end
					
				endcase

			end
	
			//Must be at end so we overrule other stuff
			if(stall_for_mem_rs || (stall_for_mem_rt && !execute_memread)) begin
				`ifdef UTICA_TRACE_STALL
					$display("[UticaCPU] Need data from memory bus, stalling");
				`endif
				
				//Overrule any side-effects we might have had on the data or register bus
				mem_regwrite <= 0;
				mem_rdval <= 0;
				mem_lb <= 0;
				mem_lbu <= 0;
				mem_lh <= 0;
				mem_lhu <= 0;
				mem_lw <= 0;
				mem_overflow_exception <= 0;
				dbus_wr <= 0;
				dbus_rd <= 0;
				dbus_dout <= 0;
				
				//Fetch/decode will bubble us out
				
			end

			//Clean up if we're jumping
			if(execute_jumping) begin
				dbus_addr <= 0;
				dbus_wmask <= 0;
				dbus_wr <= 0;
				dbus_rd <= 0;
				dbus_dout <= 0;
			end
		
		end	
	end

endmodule
